mod hmap;
mod map;

use crate::{Backend, RespArray, RespError, RespFrame, SimpleString};
use enum_dispatch::enum_dispatch;
use lazy_static::lazy_static;
use thiserror::Error;

lazy_static! {
    static ref RSP_OK: RespFrame = SimpleString::new("OK").into();
}

#[derive(Debug, Error)]
pub enum CommandError {
    #[error("Invalid command: {0}")]
    InvalidCommand(String),
    #[error("Invalid argument: {0}")]
    InvalidArgs(String),
    #[error("{0}")]
    RespError(#[from] RespError),
    #[error("Frome utf8 error")]
    FromUtf8Error(#[from] std::string::FromUtf8Error),
}

#[enum_dispatch]
pub trait CommandExecutor {
    fn execute(self, backend: &Backend) -> RespFrame;
}

#[enum_dispatch(CommandExecutor)]
#[derive(Debug)]
pub enum Command {
    Get(Get),
    Set(Set),
    HGet(HGet),
    HSet(HSet),
    HGetAll(HGetAll),
    NotSupport(NotSupport),
}

#[allow(dead_code)]
#[derive(Debug)]
pub struct Get {
    key: String,
}

#[allow(dead_code)]
#[derive(Debug)]
pub struct Set {
    key: String,
    value: RespFrame,
}

#[allow(dead_code)]
#[derive(Debug)]
pub struct HGet {
    key: String,
    field: String,
}

#[allow(dead_code)]
#[derive(Debug)]
pub struct HSet {
    key: String,
    field: String,
    value: RespFrame,
}

#[allow(dead_code)]
#[derive(Debug)]
pub struct HGetAll {
    key: String,
}

#[derive(Debug)]
pub struct NotSupport;

impl TryFrom<RespFrame> for Command {
    type Error = CommandError;
    fn try_from(value: RespFrame) -> Result<Self, Self::Error> {
        match value {
            RespFrame::Array(array) => array.try_into(),
            _ => Err(CommandError::InvalidCommand(
                "Command must be an array".to_string(),
            )),
        }
    }
}

impl TryFrom<RespArray> for Command {
    type Error = CommandError;
    fn try_from(value: RespArray) -> Result<Self, Self::Error> {
        match value.first() {
            Some(RespFrame::BulkString(ref cmd)) => {
                match String::from_utf8_lossy(cmd.as_ref()).as_bytes() {
                    b"get" => Ok(Get::try_from(value)?.into()),
                    b"set" => Ok(Set::try_from(value)?.into()),
                    b"hget" => Ok(HGet::try_from(value)?.into()),
                    b"hset" => Ok(HSet::try_from(value)?.into()),
                    b"hgetall" => Ok(HGetAll::try_from(value)?.into()),
                    _ => Ok(NotSupport.into()),
                }
            }
            Some(frame) => Err(CommandError::InvalidCommand(format!("cmd: {:?}", frame))),
            None => Err(CommandError::InvalidArgs(
                "try form resp array failed".to_string(),
            )),
        }
    }
}

fn validate_command(
    value: &RespArray,
    name: &'static str,
    n_args: usize,
) -> Result<(), CommandError> {
    if value.len() != n_args + 1 {
        return Err(CommandError::InvalidCommand(format!(
            "{} command must have exactly {} argument",
            name, n_args
        )));
    }

    match value[0] {
        RespFrame::BulkString(ref cmd) => {
            if cmd.to_vec() != name.as_bytes() {
                Err(CommandError::InvalidCommand(
                    "GET command must have a BulkString as the first argument".to_string(),
                ))
            } else {
                Ok(())
            }
        }
        _ => Err(CommandError::InvalidCommand(
            "GET command must have a BulkString as the first argument".to_string(),
        )),
    }
}

impl CommandExecutor for NotSupport {
    fn execute(self, _backend: &Backend) -> RespFrame {
        RSP_OK.clone()
    }
}

// 使用了enum_dipspatch后, 下面的代码无需事项, 由enum_dipspatch自动实现减少重复代码
// impl From<Get> for Command {
//     fn from(value: Get) -> Self {
//         Command::Get(value)
//     }
// }

// impl From<Set> for Command {
//     fn from(value: Set) -> Self {
//         Command::Set(value)
//     }
// }

// impl From<HGet> for Command {
//     fn from(value: HGet) -> Self {
//         Command::HGet(value)
//     }
// }

// impl From<HGetAll> for Command {
//     fn from(value: HGetAll) -> Self {
//         Command::HGetAll(value)
//     }
// }

// impl From<HSet> for Command {
//     fn from(value: HSet) -> Self {
//         Command::HSet(value)
//     }
// }

// impl CommandExecutor for Command {
//     fn execute(self, backend: &Backend) -> RespFrame {
//         match self {
//             Command::Get(get) => get.execute(backend),
//             Command::Set(set) => set.execute(backend),
//             Command::HGetAll(hgetall) => hgetall.execute(backend),
//             Command::HSet(hset) => hset.execute(backend),
//             Command::HGet(hget) => hget.execute(backend),
//         }
//     }
// }
